
#ifndef IVARIANTSTACK_H
#define IVARIANTSTACK_H

#include <BVariant>
#include <BArray>

namespace BWE
{
	class IVariantStack : public BThing
	{
	public:
		inline IVariantStack() : _def()
		{
			_size = 0;
			_capa = 0;
			_data = 0;
		}
		inline IVariantStack(int size, const BVariant& v = BVariant()) : _def()
		{
			_size = 0;
			_capa = 0;
			_data = 0;
			push(v, size);
		}
		inline IVariantStack(const IVariantStack& other) : _def()
		{
			_size = 0;
			_capa = 0;
			_data = 0;
			_allocate(other._size);
			for (int i = 0; i < other._size; i++)
			{
				::new(_data + i)BVariant(other._data[i]);
			}
		}
		inline ~IVariantStack()
		{
			if (_data)
			{
				for (int i = 0; i < _size; i++)
					_data[i].~BVariant();
				free(_data);
				_size = 0;
				_capa = 0;
				_data = 0;
			}
		}

		inline bool empty() const
		{
			return _size == 0;
		}
		inline int size() const
		{
			return _size;
		}
		inline int capacity() const
		{
			return _capa;
		}
		inline bool reserve(int size)
		{
			if (size < 0)
				return false;
			if (_size == size)
				return true;
			if (size > _size)
			{
				if (!_resize(size))
					return false;
			}
			if (size < _size)
			{
				int count = _size - size;
				while (count)
					_data[size + --count].~BVariant();
				if (!_resize(size))
					return false;
			}
			return true;
		}

		inline void reset()
		{
			for (int i = 0; i < _size; i++)
				_data[i].~BVariant();
			_size = 0;
		}
		inline void clear()
		{
			if (_data)
			{
				for (int i = 0; i < _size; i++)
					_data[i].~BVariant();
				free(_data);
				_data = 0;
				_capa = 0;
				_size = 0;
			}
		}

		inline bool check(int index) const
		{
			return -1 < index && index < _size;
		}
		inline void correct(int& index) const
		{
			if (index < 0)
				index = 0;
			if (index > _size - 1)
				index = _size - 1;
		}

		inline bool set(int pos, const BVariant& value)
		{
			if (pos < 0 || _size <= pos)
				return false;
			_data[pos].~BVariant();
			::new(_data + pos)BVariant(value);
			return true;
		}
		inline bool set(int pos, const BVariant* data, int size)
		{
			if (pos < 0 || pos >= _size)
				return false;
			if (pos + size > _size)
				size = _size - pos;
			for (int i = 0; i < size; i++)
			{
				_data[pos + i].~BVariant();
				::new(_data + pos + i)BVariant(data[i]);
			}
			return true;
		}
		inline bool set(int pos, const IVariantStack& other)
		{
			if (pos < 0 || pos >= _size)
				return false;
			int size = other.size();
			if (pos + size > _size)
				size = _size - pos;
			for (int i = 0; i < size; i++)
			{
				_data[pos + i].~BVariant();
				::new(_data + pos + i)BVariant(other._data[i]);
			}
			return true;
		}

		template<class UT>
		inline int find(const UT& value, int pos = 0) const
		{
			if (0 <= pos && pos < _size)
			{
				while (pos < _size)
				{
					if (_data[pos] == value)
						break;
					pos++;
				}
			}
			return pos;
		}

		template<class UT>
		inline int rfind(const UT& value, int pos = -1) const
		{
			if (pos < 0 || pos >= _size)
				pos = _size - 1;
			while (pos >= 0)
			{
				if (_data[pos] == value)
					break;
				pos--;
			}
			return pos;
		}

		template<class UT>
		inline int count(const UT& value) const
		{
			int counter = 0;
			for (int i = 0; i < _size; i++)
			{
				if (_data[i] == value)
					counter++;
			}
			return counter;
		}

		template<class UT>
		inline bool contain(const UT& value) const
		{
			for (int i = 0; i < _size; i++)
			{
				if (_data[i] == value)
					return true;
			}
			return false;
		}

		inline BVariant* data()
		{
			return _data;
		}
		inline const BVariant* data() const
		{
			return _data;
		}

		inline BVariant& def()
		{
			return _def;
		}
		inline const BVariant& def() const
		{
			return _def;
		}

		inline BVariant& push()
		{
			if (_allocate(1))
			{
				::new (_data + _size)BVariant();
				_size++;
				return _data[_size - 1];
			}
			return _def;
		}
		inline BVariant& push(const BValue& value)
		{
			if (_allocate(1))
			{
				::new (_data + _size)BVariant();
				_data[_size].setValue(value);
				_size++;
				return _data[_size - 1];
			}
			return _def;
		}
		inline BVariant& push(const BVariant& var)
		{
			if (_allocate(1))
			{
				::new (_data + _size)BVariant(var);
				_size++;
				return _data[_size - 1];
			}
			return _def;
		}
		inline bool push(const BVariant& var, int size)
		{
			if (size < 1)
				return false;
			if (_allocate(size))
			{
				for (int i = 0; i < size; i++)
					::new (_data + _size + i)BVariant(var);
				_size += size;
				return true;
			}
			return false;
		}
		inline bool push(const BVariant* vars, int size)
		{
			if (!vars || size < 1)
				return false;
			if (_allocate(size))
			{
				for (int i = 0; i < size; i++)
					::new (_data + _size + i)BVariant(vars[i]);
				_size += size;
				return true;
			}
			return false;
		}

		inline bool back(int size)
		{
			if (size < 0)
				return false;
			if (_size == size)
				return true;
			if (_size > size)
			{
				int count = _size - size;
				while (count)
					_data[size + --count].~BVariant();
			}
			_size = size;
			return true;
		}

		inline BVariant& first()
		{
			if (_data)
				return _data[0];
			return _def;
		}
		inline const BVariant& first() const
		{
			if (_data)
				return _data[0];
			return _def;
		}

		inline BVariant& last()
		{
			if (_data)
				return _data[_size - 1];
			return _def;
		}
		inline const BVariant& last() const
		{
			if (_data)
				return _data[_size - 1];
			return _def;
		}

		inline BVariant& operator()(int index)
		{
			if (_data && -1 < index && index < _size)
				return _data[index];
			return _def;
		}
		inline const BVariant& operator()(int index) const
		{
			if (_data && -1 < index && index < _size)
				return _data[index];
			return _def;
		}

		inline BVariant& operator[](int index)
		{
			return _data[index];
		}
		inline const BVariant& operator[](int index) const
		{
			return _data[index];
		}

		template<class UT>
		inline IVariantStack& operator << (const UT& value)
		{
			this->push(value);
			return *this;
		}

	private:
		inline int _capa_hint(int size)
		{
			if (size <= 0)
				return 0;
			int bit = 1;
			while (size >> ++bit);
			int capa = 1 << bit;
			return capa;
		}
		inline bool _allocate(int count)
		{
			if (count < 1)
				return false;

			int size = _size + count;
			if (_capa >= size)
				return true;

			int capa = _capa_hint(size);

			if (!_data)
			{
				_data = (BVariant*)malloc(capa * sizeof(BVariant));
				if (!_data)
					return false;
				_capa = capa;
				return true;
			}

			BVariant* temp = (BVariant*)realloc(_data, capa * sizeof(BVariant));
			if (!temp)
				return false;
			_data = temp;
			_capa = capa;
			return true;
		}
		inline bool _release(int count)
		{
			if (count < 1)
				return false;

			int size = _size - count;
			if (size < 1)
			{
				if (_data)
				{
					free(_data);
					_data = 0;
					_capa = 0;
				}
				return true;
			}

			int capa = _capa_hint(size);
			if (capa == _capa)
				return true;

			BVariant* temp = (BVariant*)malloc(capa * sizeof(BVariant));
			if (!temp)
				return false;
			memcpy(temp, _data, size * sizeof(BVariant));
			free(_data);
			_data = temp;
			_capa = capa;
			return true;
		}
		inline bool _resize(int size)
		{
			if (size < 0)
				return false;
			if (size == 0)
			{
				if (_data)
				{
					free(_data);
					_data = 0;
					_capa = 0;
				}
				return true;
			}
			if (size > _capa)
			{
				BVariant* temp = (BVariant*)realloc(_data, size * sizeof(BVariant));
				if (!temp)
					return false;
				_data = temp;
				_capa = size;
				return true;
			}
			if (size < _capa)
			{
				BVariant* temp = (BVariant*)malloc(size * sizeof(BVariant));
				if (!temp)
					return false;
				memcpy(temp, _data, size * sizeof(BVariant));
				free(_data);
				_data = temp;
				_capa = size;
				return true;
			}
			return true;
		}

	private:
		int			_size;
		int			_capa;
		BVariant* _data;
		BVariant		_def;
	};

}

#endif
